﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using MongoDB.Bson.Serialization;
using MongoScienceDAL.Core.Exceptions;
using Moq;

namespace MongoScienceDAL.Core.Tests
{
    [TestClass]
    public class ScienceDatabaseTests
    {
        #region Mongo Related Tests
        
        [TestMethod]
        public void CreatingScienceDatabaseRegistersClassMappingsForBuiltinTypes()
        {
            TestHarness.ResetDatabase();
            var database = TestHarness.DataSource;

            Assert.IsTrue(BsonClassMap.IsClassMapRegistered(typeof(ScienceCollection)));
            Assert.IsTrue(BsonClassMap.IsClassMapRegistered(typeof(ScienceType)));
        }

        #endregion

        #region Restricted Collection Names Tests

        [TestMethod]
        public void RestrictedCollectionNamesContainsInitialValuesAndReasons()
        {
            TestHarness.ResetDatabase();
            var database = TestHarness.DataSource;

            const string buildInReason = "Built in Collection for MongoScienceDAL";

            Assert.IsTrue(database.RestrictedCollectionNames.Count == 2);
            Assert.IsTrue(database.RestrictedCollectionNames.ContainsKey(TestHarness.ScienceTypesCollectionName));
            Assert.IsTrue(database.RestrictedCollectionNames.ContainsKey(TestHarness.ScienceCollectionsCollectionName));
            Assert.AreEqual(database.RestrictedCollectionNames[TestHarness.ScienceTypesCollectionName], buildInReason);
            Assert.AreEqual(database.RestrictedCollectionNames[TestHarness.ScienceCollectionsCollectionName], buildInReason);
        }

        [TestMethod]
        public void AddingCollectionAutomaticallyAddsToTheRestrictedNames()
        {
            //clear existing
            TestHarness.ResetDatabase();
            var database = TestHarness.DataSource;

            database.AddCollection(new ScienceCollection() { Name = "test1" });

            Assert.IsTrue(database.RestrictedCollectionNames.ContainsKey("test1"));
            Assert.AreEqual(database.RestrictedCollectionNames["test1"], "Collection already exists.");
        }

        [TestMethod]
        public void RestrictedNamesReadsExistingCollections()
        {
            //clear existing
            TestHarness.ResetDatabase();
            var database = TestHarness.DataSource;
            database.AddCollection(new ScienceCollection { Name = "test1" });

            var database2 = TestHarness.DataSource;
            Assert.IsTrue(database2.RestrictedCollectionNames.ContainsKey("test1"));
            Assert.AreEqual(database2.RestrictedCollectionNames["test1"], "Collection already exists.");
        }

        #endregion
        
        #region Add / Query Collections Tests

        [TestMethod]
        public void AddNewCollectionAddsCollectionAndReturnsId()
        {
            //Clear the dataSource
            TestHarness.ResetDatabase();
            var database = TestHarness.DataSource;

            var scienceCollection = new ScienceCollection
                {
                    Index = 1,
                    CreatedBy = "testuser",
                    CreatedOn = new DateTime(1985,5,23),
                    Description = "test collection",
                    Name = "test1"
                };

            scienceCollection = database.AddCollection(scienceCollection);

            //Get manual connection to check
            var mongoDatabase = TestHarness.DataSource.Database;
            
            Assert.IsTrue(mongoDatabase.CollectionExists(TestHarness.ScienceCollectionsCollectionName));

            var collection = mongoDatabase.GetCollection(TestHarness.ScienceCollectionsCollectionName);
            var myType = collection.FindOne();

            Assert.IsTrue(myType.Contains("name"));
            Assert.AreEqual(myType["name"].AsString, "test1");
            Assert.IsTrue(myType.Contains("desc"));
            Assert.AreEqual(myType["desc"].AsString, "test collection");
            Assert.IsTrue(myType.Contains("index"));
            Assert.AreEqual(myType["index"].AsInt32, 1);
            Assert.IsTrue(myType.Contains("con"));
            Assert.AreEqual(myType["con"].AsDateTime, new DateTime(1985, 5, 23).ToUniversalTime());
            Assert.IsTrue(myType.Contains("cby"));
            Assert.AreEqual(myType["cby"].AsString, "testuser");

            //check that the result has been assigned an Id
            var id = myType.GetValue("_id");
            Assert.AreEqual(scienceCollection.Id, id.AsObjectId);
        }

        [TestMethod]
        public void QueryCollectionReturnsCollections()
        {
            //Clear dataSource
            TestHarness.ResetDatabase();
            var database = TestHarness.DataSource;

            database.AddCollection(new ScienceCollection
                {
                    Index = 1,
                    CreatedBy = "testuser",
                    CreatedOn = DateTime.Today,
                    Description = "test collection",
                    Name = "test1"
                });
            database.AddCollection(new ScienceCollection
                {
                    Index = 2,
                    CreatedBy = "testuser",
                    CreatedOn = DateTime.Today,
                    Description = "test collection",
                    Name = "test2"
                });

            var collections = database.QueryCollections();

            Assert.AreEqual(collections.Count(), 2);

            var queried = collections.Where(x => x.Name == "test2");

            Assert.AreEqual(queried.Count(), 1);
        }

        [TestMethod]
        [ExpectedException(typeof(InvalidCollectionNameException))]
        public void AddingAExistingCollectionThrowsAndException()
        {
            TestHarness.ResetDatabase();
            var database = TestHarness.DataSource;

            database.AddCollection(new ScienceCollection() {Name = "test1"});
            database.AddCollection(new ScienceCollection() {Name = "test1"}); //should throw
        }

        #endregion

        #region Add / Query Collection Types Tests

        [TestMethod]
        [ExpectedException(typeof(NoSuchCollectionException))]
        public void AddCollectionTypeShouldThrowExceptionIfNoSuchCollectionExists()
        {
            TestHarness.ResetDatabase();
            var database = TestHarness.DataSource;

            var testType = new ScienceType
                {
                    CreatedBy = "testuser",
                    CreatedOn = new DateTime(1985, 5, 23),
                    Description = "test description",
                    Hidden = false,
                    Index = 1,
                    Key = "t1",
                    Name = "test1",
                    Required = false,
                    Settings = new Dictionary<string, string>(),
                    Type = ScienceTypeEnum.Text
                };

            database.AddCollectionType("dosentExist", testType);
        }

        [TestMethod]
        [ExpectedException(typeof(NoSuchCollectionException))]
        public void QueryCollectionTypeShouldThrowExceptionIfNoSuchCollectionExists()
        {
            TestHarness.ResetDatabase();
            var database = TestHarness.DataSource;

            var types = database.QueryCollectionTypes("dosentExist");
        }

        [TestMethod]
        public void AddTypeAddsTheTypeAndReturnsTheCorrectId()
        {
            TestHarness.ResetDatabase();
            var database = TestHarness.DataSource;

            //add collection for test
            database.AddCollection(new ScienceCollection() {Name = "testCollection"});

            var testType = new ScienceType
            {
                CreatedBy = "testuser",
                CreatedOn = new DateTime(1985, 5, 23),
                Description = "test description",
                Hidden = false,
                Index = 1,
                Key = "t1",
                Name = "test1",
                Required = false,
                Settings = new Dictionary<string, string>(),
                Type = ScienceTypeEnum.Text
            };

            //add type to collection
            testType = database.AddCollectionType("testCollection", testType);

            var mongoDatabase = TestHarness.DataSource.Database;

            Assert.IsTrue(mongoDatabase.CollectionExists(TestHarness.ScienceTypesCollectionName));

            var rawCollection = mongoDatabase.GetCollection<ScienceType>(TestHarness.ScienceTypesCollectionName);

            Assert.IsTrue(rawCollection.Count() > 0);

            var onlyItem = rawCollection.FindOne();

            Assert.AreEqual(onlyItem.Id, testType.Id);
        }

        [TestMethod]
        public void QueryCollectionTypeShouldQueryExistingCollectionTypes()
        {
            TestHarness.ResetDatabase();
            var database = TestHarness.DataSource;

            database.AddCollection(new ScienceCollection {Name = "testCollection"});

            var testType1 = database.AddCollectionType("testCollection",new ScienceType
            {
                CreatedBy = "testuser",
                CreatedOn = new DateTime(1985, 5, 23),
                Description = "test description",
                Hidden = false,
                Index = 1,
                Key = "t1",
                Name = "test1",
                Required = false,
                Settings = new Dictionary<string, string>(),
                Type = ScienceTypeEnum.Text
            });

            var testType2 = database.AddCollectionType("testCollection", new ScienceType
            {
                CreatedBy = "testuser",
                CreatedOn = new DateTime(1985, 5, 23),
                Description = "test description",
                Hidden = false,
                Index = 2,
                Key = "t2",
                Name = "test2",
                Required = false,
                Settings = new Dictionary<string, string>(),
                Type = ScienceTypeEnum.Text
            });

            var result = database.QueryCollectionTypes("testCollection");

            Assert.AreEqual(result.Count(),2);

            result = result.Where(x => x.Key == "t2");

            Assert.AreEqual(result.Count(), 1);

            var firstResult = result.First();

            Assert.AreEqual(firstResult.Index, 2);
        }

        #endregion
    }
}
